﻿using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
using System;
using System.Linq;
using System.Reflection;
using Extensions;

namespace TC
{
    public class FlowGraphSurface : GraphSurface<FlowGraph>
    {
        public Dictionary<Node, FlowNodeSurface> nodeSurfaceDic;
        public Dictionary<PortConnection, ConnectSurface> connectSurfaceDic;
        public List<CommentSurface> commentSurfaceList;
        public List<FlowNodeSurface> tmpContextNodeSurf = new List<FlowNodeSurface>();// 当前操作的环境变量对应的flowNodeSurf

        public bool willRepaint = true;
        private Rect viewRect;                                          // 可视窗口相对Editor的位置，实际拖动的是relativeRect
        public Color gridLineColor = new Color(0.6f, 0.6f, 0.6f, 0.3f);


        
        private FlowNodeSurface cachedActiveNodeSurf;
        public FlowNodeSurface activeNodeSurf { get; set; }             // 一个Graph同一时间只会有一个active node 和 port
        public PortSurface activePortSurf { get; set; }                 // ActivePortSurf：为了绘制正在连接的Connection而存在
        public ConnectSurface activeConnectSurf { get; set; }


        public FlowGraphSurface(FlowGraph t) : base(t)
        {
            nodeSurfaceDic = new Dictionary<Node, FlowNodeSurface>();
           
            connectSurfaceDic = new Dictionary<PortConnection, ConnectSurface>();
            position = new Vector2(-5000f, -5000f);
        }


        #region 显示刷新
        public override void OnDraw(Rect viewRect)
        {
            this.viewRect = viewRect;
            GUI.BeginClip(viewRect);

            relativeRect.position = position;
            relativeRect.size = (viewRect.size + position.Abs());
            DrawBgGrid();
            GUI.BeginClip(relativeRect);

            // 画Node
            foreach (var i in nodeSurfaceDic)
            {
                i.Value.OnDraw(relativeRect);
            }

            foreach (var i in connectSurfaceDic)
            {
                i.Value.OnDraw(relativeRect);
            }

            // 画正在拖拽的贝塞尔曲线
            DrawDraggingBezier();

            GUI.EndClip();
            GUI.EndClip();
        }

        private void DrawBgGrid()
        {
            float step = ((curScale > 0.6) ? 30 : 80) * curScale;
            Handles.color = gridLineColor;
            for (float i = relativeRect.yMin; i < relativeRect.yMax; i += step)
            {
                Handles.DrawLine(new Vector2(0, i), new Vector2(relativeRect.xMax, i));
            }
            for (float i = relativeRect.xMin; i < relativeRect.xMax; i += step)
            {
                Handles.DrawLine(new Vector2(i, 0), new Vector2(i, relativeRect.yMax));
            }
        }

        public void DrawDraggingBezier()
        {
            if (activePortSurf != null)
            {
                var portCenterPos = activePortSurf.portRelativeRect.position + activePortSurf.portRelativeRect.size / 2;
                var endPos = (TreeViewMenu.instance.editorWindow != null) ?
                    TreeViewMenu.instance.position - (relativeRect.position + viewRect.position) : Event.current.mousePosition;
                if (!activePortSurf.target.IsInput)
                {
                    BezierUtil.DrawBezier(portCenterPos, endPos, activePortSurf.portColor, true);
                }
                if (activePortSurf.target.IsInput)
                {
                    BezierUtil.DrawBezier(endPos, portCenterPos, activePortSurf.portColor, true);
                }
                //这里区分不同port绘制不同样式的曲线，
                //if (activePortSurf.target is FlowInput)
                //if (activePortSurf.target is FlowOutput)
            }
        }

        public override void RefreshScale(float newScale, Vector2 mousePosition)
        {
            Vector2 newPos = mousePosition + (position - mousePosition) / curScale * newScale;
            if (newPos.x <= viewRect.position.x && newPos.y <= viewRect.position.y)
            {
                position = newPos;
                curScale = newScale;
                foreach (var item in nodeSurfaceDic)
                {
                    item.Value.RefreshScale(curScale, (mousePosition - relativeRect.position) / curScale * maxScale);
                }
            }

        }
        #endregion

        #region 事件检测处理

        public override void OnEvent()
        {
            base.OnEvent();
            if (cachedActiveNodeSurf != activeNodeSurf)
            {
                cachedActiveNodeSurf = activeNodeSurf;

                if (activeNodeSurf != null)
                { EventCenter.Instance.SendEvent<FlowNodeSurface>((int)EventCenterNumber.OnActiveNodeChg, activeNodeSurf); }
                else
                { EventCenter.Instance.SendEvent<FlowNodeSurface>((int)EventCenterNumber.OnActiveNodeChg, null); }
            }
        }

        public override void ChildrenEventHandler()
        {
            base.ChildrenEventHandler();
            Event.current.mousePosition -= (viewRect.position + relativeRect.position);
            // 反序遍历，因为绘制的时候，后加入的node显示在上层
            for (int i = connectSurfaceDic.Count - 1; i >= 0; i--)
            {
                var item = connectSurfaceDic.ElementAt(i);
                item.Value.OnEvent();
            }
            for (int i = nodeSurfaceDic.Count - 1; i >= 0; i--)
            {
                var item = nodeSurfaceDic.ElementAt(i);
                item.Value.OnEvent();
            }
            Event.current.mousePosition += (viewRect.position + relativeRect.position);
        }

        public override void KeyCodeEventHandler()
        {
            Event e = Event.current;
            if (e.keyCode == KeyCode.Delete)
            {
                if (activeConnectSurf != null)
                {
                    RemoveConnectSurface(activeConnectSurf);
                    activeConnectSurf = null;
                    e.Use();
                    willRepaint = true;
                    return;
                }

                if (activeNodeSurf != null)
                {
                    RemoveNodeSurface(activeNodeSurf);
                    activeNodeSurf = null;
                    e.Use();
                    willRepaint = true;
                    return;
                }
            }

            // TODO 复制粘贴
            if (Application.platform == RuntimePlatform.WindowsEditor)
            {
                if (Event.current.Equals(Event.KeyboardEvent("^x")))
                {
                    //EditorGUIUtility.systemCopyBuffer = activeNodeSurf.target.OnSerialize_t();
                }
                if (Event.current.Equals(Event.KeyboardEvent("^c"))) { }
                if (Event.current.Equals(Event.KeyboardEvent("^v"))) { }
            }
            else if(Application.platform == RuntimePlatform.OSXEditor)
            {
                if (Event.current.Equals(Event.KeyboardEvent("%x"))) { }
                if (Event.current.Equals(Event.KeyboardEvent("%c"))) { }
                if (Event.current.Equals(Event.KeyboardEvent("%v"))) { }
            }
        }

        public override void MouseDownEventHandler()
        {
            ResetActiveElements();

            Event e = Event.current;
            // 此处暂时只检测左键事件，activeNode/activePort都是在左键MouseDown时产生
            if (e.button == 0)
            {
                for (int i = connectSurfaceDic.Count - 1; i >= 0; i--)
                {
                    // 监测是否被点击
                    var item = connectSurfaceDic.ElementAt(i);
                    if (item.Value.isContaining)
                    {
                        activeConnectSurf = item.Value;
                        activeConnectSurf.isActive = true;
                        e.Use();
                        willRepaint = true;
                        break;
                    }
                }

                for (int i = nodeSurfaceDic.Count - 1; i >= 0; i--)
                {
                    var nodeSurf = nodeSurfaceDic.ElementAt(i).Value;
                    var clickedportSurf = nodeSurf.GetActivePortSurf();
                    if (clickedportSurf != null)
                    {
                        activePortSurf = clickedportSurf;
                        if (activeConnectSurf != null)
                        {
                            if (activeConnectSurf.GetOther(activePortSurf) != null)
                            {
                                activeConnectSurf.isActive = false;
                                activeConnectSurf = null;
                            }
                            else { activePortSurf = null; }
                        }
                        e.Use();
                        willRepaint = true;

                        //回调每个Port，每个port会有自己的逻辑，包括检查和绘制
                        //也可以回调Node，把检查的逻辑放在node里
                        return;
                    }
                    if (nodeSurf.isContaining && activeConnectSurf == null)
                    {
                        activeNodeSurf = nodeSurf;
                        activeNodeSurf.isActive = true;
                        e.Use();
                        willRepaint = true;
                        return;
                    }
                }
            }
        }

        public override void MouseDragEventHandler()
        {
            Event e = Event.current;
            if (e.button == 0)
            {
                if (activeNodeSurf != null)
                {
                    activeNodeSurf.position += e.delta;
                    e.Use();
                    willRepaint = true;
                }
                if (activePortSurf != null)
                {
                    //需要判断当前进入了哪个ui，做相应的逻辑
                    e.Use();
                    willRepaint = true;
                }
            }
            else if(e.button == 2)
            {
                position += e.delta;
                if (position.x > viewRect.position.x) { position = new Vector2(viewRect.position.x, position.y); }
                if (position.y > viewRect.position.y) { position = new Vector2(position.x, viewRect.position.y); }
                e.Use();
                willRepaint = true;
            }
        }

        public override void MouseUpEventHandler()
        {
            Event e = Event.current;

            for (int i = nodeSurfaceDic.Count - 1; i >= 0; i--)
            {
                var nodeSurf = nodeSurfaceDic.ElementAt(i).Value;
                var clickedportSurf = nodeSurf.GetActivePortSurf();
                if (clickedportSurf != null)
                {
                    //左键拖拽，松手时连接两个点
                    if (e.button == 0 && clickedportSurf != activePortSurf)
                    {
                        ConnectionCreateHandler(clickedportSurf);
                    }
                    e.Use();
                    willRepaint = true;
                    activePortSurf = null;
                    return;
                }
                if (nodeSurf.isContaining && ((e.button == 0 && activePortSurf != null) ||(e.button == 1 && activePortSurf == null)))
                {
                    Debug.Log("无法在此处创建一个新的节点");
                    e.Use();
                    willRepaint = true;
                    activePortSurf = null;
                    return;
                }
            }

            if (e.button == 1 || (e.button == 0 && activePortSurf != null))
            {
                TreeViewMenu.instance.ReConstructTree(e.mousePosition, this, target, activePortSurf);
            }

            e.Use();
            willRepaint = true;
            activePortSurf = null;
        }

        public override void UsedEventHandler()
        {
            base.UsedEventHandler();
            ResetActiveElements(); // 事件被子元素use了，需要此时需要重置活跃元素
        }

        public void ResetActiveElements()
        {
            if (activeConnectSurf != null)
            {
                activeConnectSurf.isActive = false;
                activeConnectSurf = null;
                willRepaint = true;
            }
            if (activeNodeSurf != null)
            {
                activeNodeSurf.isActive = false;
                activeNodeSurf = null;
                willRepaint = true;
            }
            activePortSurf = null;
        }

        public override void NodeMenuEventHandler(Vector2 position, Type nodeType, ValueWrap contextVar = default)
        {
            FlowNodeSurface nodeSurface = AddNodeSurface(nodeType, position, contextVar);
            if (activePortSurf != null)
            {
                FieldInfo portInfo;
                if (activePortSurf.target.CanConnect(nodeType, out portInfo))
                {
                    var port = portInfo.GetValue(nodeSurface.target);
                    var portSurface = nodeSurface.GetPortSurface((Port)port);
                    ConnectionCreateHandler(portSurface);
                }
                activePortSurf = null;
            }
            willRepaint = true;
        }

        public override void ScrollWheelEventHandler()
        {
            Event e = Event.current;
            float newScale = curScale;
            if (e.delta.y < 0)
            {
                // 放大
                newScale += scaleStep;
                if (newScale > maxScale) { newScale = maxScale; }
            }
            else if (e.delta.y > 0)
            {
                // 缩小
                newScale -= scaleStep;
                if (newScale < minScale) { newScale = minScale; }
            }

            RefreshScale(newScale, e.mousePosition - viewRect.position);
            e.Use();
            willRepaint = true;
        }
        #endregion

        #region Connection相关处理

        public PortSurface GetOtherPort(PortSurface portSurface, out ConnectSurface connectSurface)
        {
            PortSurface target;
            foreach (var item in connectSurfaceDic)
            {
                target = item.Value.GetOther(portSurface);
                if (target != null)
                {
                    connectSurface = item.Value;
                    return target;
                }
            }
            connectSurface = null;
            return null;
        }

        public void ConnectionCreateHandler(PortSurface targetPortSurf)
        {
            if (activePortSurf == null) { return; }

            if (!targetPortSurf.hasConnect || (targetPortSurf.hasConnect && targetPortSurf.target.AllowMultiConnect))
            {
                if (activePortSurf.hasConnect && !activePortSurf.target.AllowMultiConnect)
                {
                    ConnectSurface connectSurface;
                    if (targetPortSurf == GetOtherPort(activePortSurf, out connectSurface))
                    {
                        Debug.Log("是同一个端口，不要重复连接");
                        activePortSurf = null;
                        return;
                    }
                    RemoveConnectSurface(connectSurface);
                }
                if (AddConnectionSurface(activePortSurf, targetPortSurf) == false) { Debug.Log("无法连接"); }
            }
            else
            {
                Debug.Log("目标端口不允许多连接");
            }
            activePortSurf = null;
        }

        public bool AddConnectionSurface(PortSurface portSurf_1, PortSurface portSurf_2)
        {
            if (!portSurf_1.CanConnect(portSurf_2))
            {
                return false;
            }

            PortConnection connection = null;
            if (portSurf_1.target is FlowPort)
            {
                connection = target.AddConnection((FlowPort)portSurf_1.target, (FlowPort)portSurf_2.target);
            }
            else if(portSurf_1.target is ValuePort)
            {
                connection = target.AddConnection((ValuePort)portSurf_1.target, (ValuePort)portSurf_2.target);
            }
            ConnectSurface connectSurface = new ConnectSurface(connection, portSurf_1, portSurf_2);
            connectSurface.OnCreate();
            connectSurfaceDic.Add(connection, connectSurface);
            Debug.Log("连接上了");
            return true;
        }

        public void RemoveConnectSurface(ConnectSurface connectSurface)
        {
            if (connectSurface == null) { return; }
            connectSurface.OnDestroy();
            connectSurfaceDic.Remove(connectSurface.target);
            target.RemoveConnection(connectSurface.target);
        }
        #endregion

        #region Context相关处理

        public bool IsContextVarUsing(ValueWrap contextVar)
        {
            tmpContextNodeSurf.Clear();
            foreach (var item in nodeSurfaceDic)
            {
                if (item.Value.contextVar == contextVar)
                {
                    tmpContextNodeSurf.Add(item.Value);
                }
            }
            if (tmpContextNodeSurf.Count > 0) { return true; }
            return false;
        }

        public void RemoveContextNode()
        {
            for (int i = tmpContextNodeSurf.Count - 1; i >= 0; i--)
            {
                RemoveNodeSurface(tmpContextNodeSurf[i]);
            }
            willRepaint = true;
        }

        // TODO 改变 context node type
        public void ChangeContextNodeType()
        {
            for (int i = tmpContextNodeSurf.Count - 1; i >= 0; i--)
            {
                Vector2 pos = tmpContextNodeSurf[i].position + (viewRect.position + relativeRect.position);
                ValueWrap contextVar = tmpContextNodeSurf[i].contextVar;
                Type contextValueType = VariableHelper.Convert(contextVar.type);
                Type nodeType = tmpContextNodeSurf[i].target.GetType().GetGenericTypeDefinition().MakeGenericType(contextValueType);
                AddNodeSurface(nodeType, pos, contextVar);
                RemoveNodeSurface(tmpContextNodeSurf[i]);
            }
            willRepaint = true;
        }

        #endregion

        #region Node相关处理

        public FlowNodeSurface AddNodeSurface(Type nodeType, Vector2 position, ValueWrap contextVar = default)
        {
            if (nodeType.IsSubclassOf(typeof(FlowNode)))
            {
                FlowNode node = (FlowNode)Activator.CreateInstance(nodeType);
                target.AddNode(node);

                FlowNodeSurface nodeSurface = new FlowNodeSurface(node, position - (viewRect.position + relativeRect.position), curScale, contextVar);
                nodeSurfaceDic.Add(node, nodeSurface);
                nodeSurface.OnCreate();
                return nodeSurface;
            }
            return null;
        }

        public void RemoveNodeSurface(FlowNodeSurface nodeSurface)
        {
            nodeSurface.OnDestroy();
            List<ConnectSurface> relativeConnections = new List<ConnectSurface>();

            
            foreach (var connnectItem in connectSurfaceDic)
            {
                if (connnectItem.Value.sourcePortSurf.parent == nodeSurface 
                    || connnectItem.Value.targetPortSurf.parent == nodeSurface)
                {
                    relativeConnections.Add(connnectItem.Value);
                }
            }
            foreach (ConnectSurface conSurf in relativeConnections)
            {
                RemoveConnectSurface(conSurf);
            }

            nodeSurfaceDic.Remove(nodeSurface.target);
            target.RemoveNode(nodeSurface.target);
        }
        #endregion

        #region 序列化
        public override ISerializeEntity CreateEntity()
        {
            return new FlowGraphSurface_entity();
        }

        
        public override void OnSerialize_t(ISerializeEntity entity)
        {
            base.OnSerialize_t(entity);
            
            FlowGraphSurface_entity f = entity as FlowGraphSurface_entity;

            int i = 0;
            foreach (var item in nodeSurfaceDic)
            {
                item.Key.id = i++;

                ISerializeEntity tmp0 = item.Key.CreateEntity();
                item.Key.OnSerialize_t(tmp0);
                f.node_Entities.Add((Node_entity)tmp0);

                ISerializeEntity tmp1 = item.Value.CreateEntity();
                item.Value.OnSerialize_t(tmp1);
                f.nodeSurface_Entities.Add(tmp1);
            }

            
            foreach (var item in connectSurfaceDic)
            {
            
                Connection_entity c = new Connection_entity();
                c.sourceNodeId = item.Key.source.parent.id;
                c.sourcePort = item.Key.source.name;
                c.targetNodeId = item.Key.target.parent.id;
                c.targetPort = item.Key.target.name;

                c.connectType = item.Key.GetType().FullName;
                c.connectSurfaceType = item.Value.GetType().FullName;
                f.connection_Entities.Add(c);
            }

            f.vars = target.vars;
        }

        
        public override void OnDeserialize_t(ISerializeEntity entity)
        {
            base.OnDeserialize_t(entity);

            FlowGraphSurface_entity f = entity as FlowGraphSurface_entity;
            target.vars = f.vars;
            for(int i = 0; i < f.node_Entities.Count; ++i)
            {
                FlowNode node = Activator.CreateInstance(Type.GetType(f.node_Entities[i].classType)) as FlowNode;
                target.AddNode(node);
                node.OnDeserialize_t(f.node_Entities[i]);

                Surface_entity se = f.nodeSurface_Entities[i] as Surface_entity;
                FlowNodeSurface surface = Activator.CreateInstance(Type.GetType(se.classType), node, -1) as FlowNodeSurface;
                surface.OnDeserialize_t(f.nodeSurface_Entities[i]);
                nodeSurfaceDic.Add(node, surface);
            }
            
            foreach (var item in f.connection_Entities)
            {
                Port p0 = ((this.target.nodes[item.sourceNodeId]) as FlowNode).outputPorts[item.sourcePort];
                Port p1 = ((this.target.nodes[item.targetNodeId]) as FlowNode).inputPorts[item.targetPort];

                PortConnection connect = null;
                if (p0 is FlowPort)
                {
                    connect = this.target.AddConnection(p0 as FlowPort, p1 as FlowPort);
                }
                else if (p0 is ValuePort)
                {
                    connect = this.target.AddConnection(p0 as ValuePort, p1 as ValuePort);
                }

                PortSurface source = nodeSurfaceDic[p0.parent].GetPortSurface(p0);
                PortSurface target = nodeSurfaceDic[p1.parent].GetPortSurface(p1);
                //但这里应该还有问题：不支持多态
                ConnectSurface connectSurface = new ConnectSurface(connect, source, target);
                connectSurface.OnCreate();
                connectSurfaceDic.Add(connect, connectSurface);
            }
        }

        #endregion
    }
}